# remote-git-tags

借助 git ls-remotes --tags 拿到 远程的tags,然后对返回数据进行处理,以map的方式存储,便于拿取。

# 环境准备

git clone https://github.com/sindresorhus/remote-git-tags.git

# packages.json

"scripts": {
  "test": "xo && ava"
},
  "devDependencies": {
  "ava": "^3.15.0",
  "xo": "^0.44.0"
}

# index.js

import {promisify} from 'node:util';
import childProcess from 'node:child_process';

const execFile = promisify(childProcess.execFile);

export default async function remoteGitTags(repoUrl) {
	const {stdout} = await execFile('git', ['ls-remote', '--tags', repoUrl]);
	const tags = new Map();

	for (const line of stdout.trim().split('\n')) {
		const [hash, tagReference] = line.split('\t');

		// Strip off the indicator of dereferenced tags so we can override the
		// previous entry which points at the tag hash and not the commit hash
		// `refs/tags/v9.6.0^{}` → `v9.6.0`
		const tagName = tagReference.replace(/^refs\/tags\//, '').replace(/\^{}$/, '');

		tags.set(tagName, hash);
	}

	return tags;
}

# promisify

将 callback 的函数方式调用改版成 promise

node v17x promisify 实现

function promisify(original) {
  // Lazy-load to avoid a circular dependency.
  if (validateFunction === undefined)
    ({ validateFunction } = require('internal/validators'));

  validateFunction(original, 'original');

  if (original[kCustomPromisifiedSymbol]) {
    const fn = original[kCustomPromisifiedSymbol];

    validateFunction(fn, 'util.promisify.custom');

    return ObjectDefineProperty(fn, kCustomPromisifiedSymbol, {
      value: fn, enumerable: false, writable: false, configurable: true
    });
  }

  // Names to create an object from in case the callback receives multiple
  // arguments, e.g. ['bytesRead', 'buffer'] for fs.read.
  const argumentNames = original[kCustomPromisifyArgsSymbol];

  function fn(...args) {
    return new Promise((resolve, reject) => {
      // 向 args 中 push 函数
      ArrayPrototypePush(args, (err, ...values) => {
        if (err) {
          return reject(err);
        }
        if (argumentNames !== undefined && values.length > 1) {
          const obj = {};
          for (let i = 0; i < argumentNames.length; i++)
            obj[argumentNames[i]] = values[i];
          resolve(obj);
        } else {
          resolve(values[0]);
        }
      });
      ReflectApply(original, this, args);
    });
  }

  ObjectSetPrototypeOf(fn, ObjectGetPrototypeOf(original));

  ObjectDefineProperty(fn, kCustomPromisifiedSymbol, {
    value: fn, enumerable: false, writable: false, configurable: true
  });
  return ObjectDefineProperties(
    fn,
    ObjectGetOwnPropertyDescriptors(original)
  );
}

validateFunction 函数

const validateFunction = hideStackFrames((value, name) => {
  // 判断对象是否为函数
  if (typeof value !== 'function')
    throw new ERR_INVALID_ARG_TYPE(name, 'Function', value);
});

# 简单的实现

promisify 就是将 callback 的异步函数调用变成 promise 的方式进行调用,便于使用。

function promisify(original){
  function fn(...args){
    return new Promise((res,rej)=>{
      // 添加一个 callback 函数,用于源代码中进行调用,并触发 res
      args.push((err,...values)=>{
        if(err){
          return rej(err)
        }
        res(values)
      })
      // 静态方法 Reflect.apply() 通过指定的参数列表发起对目标(target)函数的调用。
      Reflect.apply(original, this, args);
    })
  }
  return fn;
}

# 参考资料